#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define LEDDEV_CNT		1			/* 设备号长度 	*/
#define LEDDEV_NAME		"platled"	/* 设备名字 	*/
#define LEDOFF 			0
#define LEDON 			1

static void __iomem *PA_CFG1;
static void __iomem *PA_DATA;

struct led_dev{
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
};

struct led_dev leddev;

void led_switch(u8 sta)
{
    u32 val = 0;
	if(sta == LEDON) {
		val = readl(PA_DATA);
		val &= ~(1 << 15);	
		writel(val, PA_DATA);
	}else if(sta == LEDOFF) {
		val = readl(PA_DATA);
		val|= (1 << 15);	
		writel(val, PA_DATA);
	}	
}

static int led_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &leddev; /* 设置私有数据  */
	return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;

	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		return -EFAULT;
	}

	ledstat = databuf[0];		/* 获取状态值 */

	if(ledstat == LEDON) {	
		led_switch(LEDON);		/* 打开LED灯 */
	} else if(ledstat == LEDOFF) {
		led_switch(LEDOFF);	/* 关闭LED灯 */
	}
	return 0;
}

static struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.write = led_write,
};

static int led_probe(struct platform_device *dev)
{
    int i=0;
    int ressize[2];
    u32 val=0;
    struct resource *ledsource[2];

    printk("led driver and device has matched!\r\n");

    for(i=0;i<2;i++){
        ledsource[i]=platform_get_resource(dev, IORESOURCE_MEM, i);
        if(!ledsource[i]){
            dev_err(&dev->dev,"No MEM resource for always on\n");
            return -ENXIO;
        }
        ressize[i] = resource_size(ledsource[i]);
    }

    PA_CFG1=ioremap(ledsource[0]->start,ressize[0]);
    PA_DATA=ioremap(ledsource[1]->start,ressize[1]);

    val=readl(PA_CFG1);
    val&=~(1<<28);
    val|=(1<<28);
    writel(val,PA_CFG1);

    /*注册字符设备驱动*/
    if(leddev.major){
        leddev.devid = MKDEV(leddev.major,0);
        register_chrdev_region(leddev.devid, LEDDEV_CNT , LEDDEV_NAME);
    }else{
        alloc_chrdev_region(&leddev.devid, 0,LEDDEV_CNT , LEDDEV_NAME);
        leddev.major = MAJOR(leddev.devid);
        //leddev.minor = MINOR(leddev.devid);
    }
    //printk("newchrled major=%d,minor=%d \r\n",leddev.major,leddev.minor);

    leddev.cdev.owner = THIS_MODULE;
    cdev_init(&leddev.cdev , &led_fops);

    cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);

    leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
    if (IS_ERR(leddev.class)){
        return PTR_ERR(leddev.class);
    }

    leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);
    if (IS_ERR(leddev.device)) {
		return PTR_ERR(leddev.device);
	}
	
	return 0;
}

static int led_remove(struct platform_device *dev)
{
    iounmap(PA_CFG1);
    iounmap(PA_DATA);

    //unregister_chrdev(LED_MAJOR, LED_NAME);
    cdev_del(&leddev.cdev);
    unregister_chrdev_region(leddev.devid, LEDDEV_CNT);
    device_destroy(leddev.class, leddev.devid);
    class_destroy(leddev.class);
    
    return 0;
}

static struct platform_driver led_driver = {
    .driver =    {
        .name   ="Ori_led",
    },
    .probe  =   led_probe,
    .remove =   led_remove,
};

static int __init leddriver_init(void)
{
    return platform_driver_register(&led_driver);
}

static void __exit leddriver_exit(void)
{
    platform_driver_unregister(&led_driver);
}

module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("HRX");
